//
//  DemoString.m
//  ocdemo
//
//  Created by JIENING ZHANG on 2019/5/5.
//  Copyright © 2019 JIENING ZHANG. All rights reserved.
//

#import "DemoString.h"
#import <UIKit/UIKit.h>

#import <CommonCrypto/CommonDigest.h>
#import <CommonCrypto/CommonCrypto.h>

@implementation DemoString

+ (void)stringFromToDate {
    
    /*
     // https://www.jianshu.com/p/a2c7e1a85192
     // http://www.unicode.org/reports/tr35/tr35-31/tr35-dates.html#Date_Format_Patterns
     [dateStringFormatter setDateFormat:@"y"]; // 2017
     [dateStringFormatter setDateFormat:@"yy"]; // 17
     [dateStringFormatter setDateFormat:@"yyy"]; // 2017
     [dateStringFormatter setDateFormat:@"yyyy"]; // 2017
     
     [dateStringFormatter setDateFormat:@"M"]; // 8
     [dateStringFormatter setDateFormat:@"MM"]; // 08
     [dateStringFormatter setDateFormat:@"MMM"]; // 8月
     [dateStringFormatter setDateFormat:@"MMMM"]; // 八月
     
     [dateStringFormatter setDateFormat:@"d"]; // 3
     [dateStringFormatter setDateFormat:@"dd"]; // 03
     [dateStringFormatter setDateFormat:@"D"]; // 215,一年中的第几天
     
     [dateStringFormatter setDateFormat:@"h"]; // 4
     [dateStringFormatter setDateFormat:@"hh"]; // 04
     [dateStringFormatter setDateFormat:@"H"]; // 16 24小时制
     [dateStringFormatter setDateFormat:@"HH"]; // 16
     
     [dateStringFormatter setDateFormat:@"m"]; // 28
     [dateStringFormatter setDateFormat:@"mm"]; // 28
     [dateStringFormatter setDateFormat:@"s"]; // 57
     [dateStringFormatter setDateFormat:@"ss"]; // 04
     
     [dateStringFormatter setDateFormat:@"E"]; // 周四
     [dateStringFormatter setDateFormat:@"EEEE"]; // 星期四
     [dateStringFormatter setDateFormat:@"EEEEE"]; // 四
     [dateStringFormatter setDateFormat:@"e"]; // 5 (显示的是一周的第几天（weekday），1为周日。)
     [dateStringFormatter setDateFormat:@"ee"]; // 05
     [dateStringFormatter setDateFormat:@"eee"]; // 周四
     [dateStringFormatter setDateFormat:@"eeee"]; // 星期四
     [dateStringFormatter setDateFormat:@"eeeee"]; // 四
     
     [dateStringFormatter setDateFormat:@"z"]; // GMT+8
     [dateStringFormatter setDateFormat:@"zzzz"]; // 中国标准时间
     
     [dateStringFormatter setDateFormat:@"ah"]; // 下午5
     [dateStringFormatter setDateFormat:@"aH"]; // 下午17
     [dateStringFormatter setDateFormat:@"am"]; // 下午53
     [dateStringFormatter setDateFormat:@"as"]; // 下午52
     
     [dateStringFormatter setDateFormat:@"ww"]; // Week of Year
     [dateStringFormatter setDateFormat:@"W"]; // Week of Month
     [dateStringFormatter setDateFormat:@"qq"]; // 第几季度，比如一季度日期显示：01
     [dateStringFormatter setDateFormat:@"qqq"]; // 第几季度，比如一季度日期显示：Q1
     */
    
    NSDateFormatter *df1 = [[NSDateFormatter alloc] init];
    df1.dateFormat = @"yyyy-MM-dd HH:mm:ss";
    
    NSString *dateStr1 = @"2019-05-05 23:32:59";
    NSDate *date1 = [df1 dateFromString:dateStr1];
    NSLog(@"string2Date: string(%@) --> date(%@)", dateStr1, date1);
    
    NSDateFormatter *df2 = [[NSDateFormatter alloc] init];
    df2.dateFormat = @"yyyy|qq";
    
    NSString *dateStr2 = @"2019|02";
    NSDate *date2 = [df2 dateFromString:dateStr2];
    NSLog(@"string2Date: string(%@) --> date(%@)", dateStr2, date2);
    
    NSDateFormatter *df3 = [[NSDateFormatter alloc] init];
    df3.dateFormat = @"yyyy/ww";
    
    NSString *dateStr3 = @"2019/10";
    NSDate *date3 = [df3 dateFromString:dateStr3];
    NSLog(@"string2Date: string(%@) --> date(%@)", dateStr3, date3);
    
    NSDate *dateNow = [NSDate date];
    NSLog(@"date2String: date(%@) --df1(%@)--> string(%@)", dateNow, df1.dateFormat, [df1 stringFromDate:dateNow]);
    NSLog(@"date2String: date(%@) --df2(%@)--> string(%@)", dateNow, df2.dateFormat, [df2 stringFromDate:dateNow]);
    NSLog(@"date2String: date(%@) --df3(%@)--> string(%@)", dateNow, df3.dateFormat, [df3 stringFromDate:dateNow]);
    
}

+ (void)stringFromToArray {
    
    NSArray *monthArr = @[
                       @"January",
                       @"February",
                       @"March",
                       @"April",
                       @"May",
                       @"June",
                       @"July",
                       @"August",
                       @"September",
                       @"October",
                       @"November",
                       @"December",
                       ];
    
    // 数组 --'分隔符'--> 字符串
    NSString *monthsStr = [monthArr componentsJoinedByString:@"|"];
    
    NSLog(@"arr2String: arr(%@) --> string(%@)", monthArr, monthsStr);
    
    // 字符串 --'分隔符'--> 数组
    NSString *weekdayStr = @"Mon-Tue-Wed-Thu-Fri-Sat-Sun";
    
    NSArray *weekdayArr =[weekdayStr componentsSeparatedByString:@"-"];
    
    NSLog(@"string2Arr: string(%@)  --> arr(%@)", weekdayStr, weekdayArr);
}

+ (void)subStringSearch {
    
    NSString *ostr = @"浔阳江头夜送客，枫叶荻花秋瑟瑟。主人下马客在船，举酒欲饮无管弦。醉不成欢惨将别，别时茫茫江浸月。忽闻水上琵琶声，主人忘归客不发。寻声暗问弹者谁，琵琶声停欲语迟。移船相近邀相见，添酒回灯重开宴。千呼万唤始出来，犹抱琵琶半遮面。转轴拨弦三两声，未成曲调先有情。弦弦掩抑声声思，似诉平生不得志。低眉信手续续弹，说尽心中无限事。轻拢慢捻抹复挑，初为《霓裳》后《六幺》。大弦嘈嘈如急雨，小弦切切如私语。嘈嘈切切错杂弹，大珠小珠落玉盘。间关莺语花底滑，幽咽泉流冰下难。冰泉冷涩弦凝绝，凝绝不通声暂歇。别有幽愁暗恨生，此时无声胜有声。银瓶乍破水浆迸，铁骑突出刀枪鸣。曲终收拨当心画，四弦一声如裂帛。东船西舫悄无言，唯见江心秋月白。沉吟放拨插弦中，整顿衣裳起敛容。自言本是京城女，家在虾蟆陵下住。十三学得琵琶成，名属教坊第一部。曲罢曾教善才服，妆成每被秋娘妒。五陵年少争缠头，一曲红绡不知数。钿头银篦击节碎，血色罗裙翻酒污。今年欢笑复明年，秋月春风等闲度。弟走从军阿姨死，暮去朝来颜色故。门前冷落鞍马稀，老大嫁作商人妇。商人重利轻别离，前月浮梁买茶去。去来江口守空船，绕船月明江水寒。夜深忽梦少年事，梦啼妆泪红阑干。我闻琵琶已叹息，又闻此语重唧唧。同是天涯沦落人，相逢何必曾相识！我从去年辞帝京，谪居卧病浔阳城。浔阳地僻无音乐，终岁不闻丝竹声。住近湓江地低湿，黄芦苦竹绕宅生。其间旦暮闻何物？杜鹃啼血猿哀鸣。春江花朝秋月夜，往往取酒还独倾。岂无山歌与村笛？呕哑嘲哳难为听。今夜闻君琵琶语，如听仙乐耳暂明。莫辞更坐弹一曲，为君翻作《琵琶行》。感我此言良久立，却坐促弦弦转急。凄凄不似向前声，满座重闻皆掩泣。座中泣下谁最多？江州司马青衫湿。";
    
    // 从头到尾 查找第一处
    NSRange rangeHead = [ostr rangeOfString:@"琵琶"];
    if(rangeHead.location != NSNotFound) {
        NSString *fstr = [ostr substringToIndex:rangeHead.location];
        NSLog(@"fstr : %@", fstr);
    }
    
    // 从末尾向开头 查找第一处
    NSRange rangeTail = [ostr rangeOfString:@"琵琶" options:NSBackwardsSearch];
    if(rangeTail.location != NSNotFound) {
        NSString *fstr = [ostr substringToIndex:rangeTail.location];
        NSLog(@"NSBackwardsSearch, fstr : %@", fstr);
    }
    
    // 替换所有命中的 琵琶 为 PiPa
    NSString *rpstr = [ostr stringByReplacingOccurrencesOfString:@"琵琶" withString:@"PiPa"];
    NSLog(@"rpstr : %@", rpstr);
    
    // 替换某个range 为 PiPa
    rpstr = [ostr stringByReplacingCharactersInRange:rangeHead withString:@"PiPa"];
    NSLog(@"stringByReplacingCharactersInRange, rpstr : %@", rpstr);
    
}

+ (void)regexSearchByString:(NSString *)ostr pattern:(NSString *)pattern {
    
    // the search string is treated as an ICU-compatible regular expression; if set, no other options can apply except NSCaseInsensitiveSearch and NSAnchoredSearch
    // regexEmail 被视为 ICU-compatible 正则表达式，除了 NSCaseInsensitiveSearch 和  NSAnchoredSearch，其他的option会被忽略
    // 所以 NSBackwardsSearch 不 work
    
    // 正则查找，返回从头到尾第一个匹配的位置
    NSRange rangeHead = [ostr rangeOfString:pattern options:NSRegularExpressionSearch|NSBackwardsSearch];
    if(rangeHead.location != NSNotFound) {
        NSString *fstr = [ostr substringToIndex:rangeHead.location];
        NSLog(@"fstr : %@", fstr);
    } else {
        NSLog(@"regex find fail : %@", pattern);
    }
    
    // 正则替换，替换所有匹配的
    NSString *rpstr = [ostr stringByReplacingOccurrencesOfString:pattern withString:@"===email-replaced===" options:NSRegularExpressionSearch range:NSMakeRange(0, ostr.length)];
    NSLog(@"rpstr : %@", rpstr);
}


+ (void)regexPredicate:ostr pattern:(NSString *)pattern {
    
    //Predicate：断言，谓语，术语的意思。
    //表示一个可以用来过滤对象集合的条件。通常，我们可以从 NSComparisonPredicate, NSCompoundPredicate, 和 NSExpression的实例直接创建predicates，但是，
    //我们也可以从格式化的string来创建predicates，这个格式化的string可以被NSPredicate的类方法解析。
    
    NSPredicate *predicateID = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", pattern];
    
    // 和 pattern 全等才算 match，包含但不全等不算
    if ([predicateID evaluateWithObject:ostr]) {
        NSLog(@"%@ match idPattern : %@", ostr, pattern);
    } else {
        NSLog(@"%@ not match idPattern : %@", ostr, pattern);
    }
}

+ (void)regexSearchByRegularExpression:(NSString *)ostr pattern:(NSString *)pattern {
    
    // NSRegularExpression
    NSError *err;
    NSRegularExpression *regular = [[NSRegularExpression alloc] initWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:&err];
    
    
    
    //返回符合约定字段的个数
    NSUInteger matchCount = [regular numberOfMatchesInString:ostr options:0 range:NSMakeRange(0, ostr.length)];
    NSLog(@"matchCount : %ld", matchCount);
    
    //返回第一个符合约定的字段
    NSTextCheckingResult *firstTcr = [regular firstMatchInString:ostr options:0 range:NSMakeRange(0, ostr.length)];
    if(firstTcr) {
        NSLog(@"firstTcr, %@", [ostr substringWithRange:firstTcr.range]);
    }
    
    //返回第一个符合约定的字段在全文中点NSRange
    //- (NSRange)rangeOfFirstMatchInString:(NSString *)string options:(NSMatchingOptions)options range:(NSRange)range;
    
    
    // 返回一个存有所有符合约定的字段的NSArray
    NSArray *results = [regular matchesInString:ostr options:0 range:NSMakeRange(0, ostr.length)];
    NSLog(@"results : %ld", results.count);
    if(results.count > 0) {
        NSUInteger index = 0;
        for(NSTextCheckingResult *tcr in results) {
            NSLog(@"tcr, %ld, %@", index, [ostr substringWithRange:tcr.range]);
            ++index;
        }
    }
    
    //枚举每个符合约定的字段，通过block对其进行操作
    [regular enumerateMatchesInString:ostr options:0 range:NSMakeRange(0, ostr.length) usingBlock:^(NSTextCheckingResult * _Nullable tcr, NSMatchingFlags flags, BOOL * _Nonnull stop) {
        NSLog(@"enumerateMatchesInString, %@", [ostr substringWithRange:tcr.range]);
    }];
    
    // @param string 匹配的字符串
    // @param options 匹配的规则
    // @param range 匹配的范围
    // @param template 替换的字符串
    // @return 返回一个替换后的字符串
    NSString *rpstr = [regular stringByReplacingMatchesInString:ostr
                                                        options:0
                                                          range:NSMakeRange(0, ostr.length)
                                                   withTemplate:@"===Replaced==="];
    NSLog(@"rpstr : %@", rpstr);
    
    // @param string 匹配的字符串
    // @param options 匹配的规则
    // @param range 匹配的范围
    // @param template 替换的字符串
    // @return 转换匹配字符串的数目
    
    NSMutableString *mostr = [NSMutableString stringWithString:ostr];
    NSUInteger rpCount = [regular replaceMatchesInString:mostr
                                                 options:0
                                                   range:NSMakeRange(0, ostr.length)
                                            withTemplate:@"===ReplacedInStr==="];
    
    NSLog(@"rpCount : %lu", rpCount);
    NSLog(@"mostr : %@", mostr);
}

+ (void)regexSearch {
    
    NSString *ostr = @"百科熊猫词条  https://baike.baidu.com/item/%E5%A4%A7%E7%86%8A%E7%8C%AB/34935?fromtitle=%E7%86%8A%E7%8C%AB&fromid=162918&fr=aladdin  panda@163.com 大熊猫（学名：Ailuropoda melanoleuca）：属于食肉目、熊科、大熊猫亚科和大熊猫属唯一的哺乳动物，头躯长1.2-1.8米，尾长10-12厘米。体重80-120千克，最重可达180千克，体色为黑白两色，它有着圆圆的脸颊，大大的黑眼圈，胖嘟嘟的身体，标志性的内八字的行走方式，也有解剖刀般锋利的爪子。是世界上最可爱的动物之一。  panda@gmail.com  com.panda@gmail (010)88886666 ，或 022-22334455，或 02912345678 511122198812068932 41112219881206893X 18660000062 17000000026 下载链接 https://www.hkd.com/register?inviteCode=M3038223359 邀请码M3038223359 有谷歌商店可搜索HKD下载";
    
    
    /*
     
     ^[A-Za-z]+$　　          //匹配由26个英文字母组成的字符串
     ^[A-Za-z0-9]+$　　       //匹配由数字和26个英文字母组成的字符串
     ^\w+$　　                //匹配由数字、26个英文字母或者下划线组成的字符串 相当于“[a-zA-Z0-9\u4E00-\u9FFF_]”
     [ ]                     //表示内容的范围； eg:“[a-zA-Z]”表示任一字母；“[ab]”表示“a”或“b”
     {}                      //表示数量的范围； eg:“{2}”表示2个；“{2,5}”表示2个到5个；“{3,}”表示至少3个；
     |                       //表示 或；eg:“a|b”表示“a”或“b”
     \num                    //num为一个整数，表示前面的表达式复制num次；eg:“(ab)\3”表示“ababab”
     \b                      //表示单词的开始或结束；
     \d                      //表示数字，相当于“[0-9]”;
     \s                      //表示任意空白符；
     
     反义字符
     \W                     //表示不是字符数字下划线或者汉字的字符；
     \S                     //表示任意不是空白符的字符；
     \D                     //表示不是数字的字符；
     \B                     //表示不是单词开头或结束的位置；
     [^x]                   //表示除“x”外的任意字符；
     [^abc]                 //表示除“a”“b”“c”外的任意字符；
    
     
     \\d                // 代表数字
     {2}                // 代表有两个
     {2,4}              // 代表有2到4个
     ?                  // 代表0或1个
     +                  // 代表至少1个
     *                  // 代表0个或多个
     ^                  // 代表以...开头
     $                  // 代表以...结束
     .                  // 代表除换行符以外的任意字符
     [\u4e00-\u9fa5]    // 匹配中文字符
     
     */
    
 
    // 身份证
    NSString *idPattern = @"(\\d{17}|\\d{14})(\\d|[xX])";
    
    // 电话号码：eg：021-68686868  0511-6868686；
    NSString *phonePattern = @"(\\d{3,4}-)\\d{7,8}";
    
    // 手机
    NSString *mobilePhonePattern = @"1\\d{10}";
    
    // 邮箱
    NSString *emailPattern = @"[a-zA-Z0-9]\\w*@[a-zA-Z0-9]+\\.[a-zA-Z]{2,4}";
    
    // url
    NSString *urlPattern = @"^(https|http)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]";
    
    
    
    [DemoString regexSearchByString:ostr pattern:emailPattern];
    
    [DemoString regexPredicate:@"51112219881206893X" pattern:idPattern];
    [DemoString regexPredicate:@"022-22334455" pattern:phonePattern];
    [DemoString regexPredicate:@"17000000026" pattern:emailPattern];

    
    [DemoString regexSearchByRegularExpression:ostr pattern:emailPattern];
}

+ (NSString *)taopasswordMatchResultWithRegex:(NSString *)regex matchString:(NSString *)matchStr {
    
    NSError *error = nil;
    NSRegularExpression *regexExp = [NSRegularExpression regularExpressionWithPattern:regex options:NSRegularExpressionCaseInsensitive error:&error];
    if (error) {
        return nil;
    }
    
    // todo:必须的？口令需要去掉中间空格
    NSString *text = [matchStr stringByReplacingOccurrencesOfString:@" " withString:@""];
    //NSString *text = matchStr;
    
    NSTextCheckingResult *matchRet = [regexExp firstMatchInString:text options:NSMatchingReportCompletion range:NSMakeRange(0, text.length)];
    
    NSString *taopassword = nil;
    if (matchRet) {
        NSRange range = [matchRet range];
        
        if(range.location != NSNotFound) {
            @try {
                taopassword = [text substringWithRange:range];
            } @catch (NSException * e) {
                taopassword = nil;
            }
        }
        
    }
    
    return taopassword;
}

+ (void)stringJoin {
    
    NSString *part1 = @"七律·长征";
    NSString *part2 = @"毛泽东";
    NSString *part3 = @"1935年10月";
    NSString *part4 = @"红军不怕远征难，万水千山只等闲。";
    NSString *part5 = @"五岭逶迤腾细浪，乌蒙磅礴走泥丸。";
    NSString *part6 = @"金沙水拍云崖暖，大渡桥横铁索寒。";
    NSString *part7 = @"更喜岷山千里雪，三军过后尽开颜。";
    
    NSString *all = [NSString stringWithFormat:@"%@\n%@\n%@\n%@\n%@\n%@\n%@\n",part1, part2, part3, part4, part5, part6, part7];
    
    NSLog(@"stringWithFormat joint : %@", all);
    
    all = part1;
    all = [all stringByAppendingString:part2];
    all = [all stringByAppendingString:part3];
    all = [all stringByAppendingString:part4];
    all = [all stringByAppendingString:part5];
    all = [all stringByAppendingString:part6];
    all = [all stringByAppendingString:part7];
    
    NSLog(@"stringByAppendingString joint : %@", all);
    
    NSMutableString *mstr = [NSMutableString string];
    [mstr appendString:part1];
    [mstr appendString:part2];
    [mstr appendString:part3];
    [mstr appendString:part4];
    [mstr appendString:part5];
    [mstr appendString:part6];
    [mstr appendString:part7];
    
    NSLog(@"NSMutableString appendString joint : %@", mstr);
}

+ (NSString*) stringTrimed:(NSString*)str4Trim {
    if(str4Trim.length == 0) {
        return @"";
    }
    
    return [str4Trim stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}

+ (NSString*) stringLeftTrimed:(NSString*)str4Trim {
    if(str4Trim.length == 0) {
        return @"";
    }
    NSString *pattern = @"^(\\s)*";
    NSString *rpstr = [str4Trim stringByReplacingOccurrencesOfString:pattern withString:@"" options:NSRegularExpressionSearch range:NSMakeRange(0, str4Trim.length)];
    
    return rpstr;
}

+ (NSString*) stringRightTrimed:(NSString*)str4Trim {
    if(str4Trim.length == 0) {
        return @"";
    }
    
    NSString *pattern = @"(\\s)*$";
    NSString *rpstr = [str4Trim stringByReplacingOccurrencesOfString:pattern withString:@"" options:NSRegularExpressionSearch range:NSMakeRange(0, str4Trim.length)];
    
    return rpstr;
}

+ (void)stringTrim {
    
    NSCharacterSet* charSet4Trim = [NSCharacterSet whitespaceAndNewlineCharacterSet];
    for (int i=0; i<65536; ++i){
        
        if ([charSet4Trim  characterIsMember:(unichar)i]) {
            NSLog(@"charSet4Trim %d", i);
        }
    }
    
    NSString *ostr = @" \t 2sapce+1tab 山外青山楼外楼 3space   ";
    NSString *trimstr = [DemoString stringTrimed:ostr];
    NSString *leftTrimstr = [DemoString stringLeftTrimed:ostr];
    NSString *rightTrimstr = [DemoString stringRightTrimed:ostr];
    
    NSLog(@"stringTrim\nostr        :|%@|\ntrimstr     :|%@|\nleftTrimstr :|%@|\nrightTrimstr:|%@|", ostr, trimstr, leftTrimstr, rightTrimstr);
}

+ (void)stringFormat {
    
    // =========== 类型格式符 =============
    
    // %@       Objective-C object, printed as the string returned by descriptionWithLocale: if available, or description otherwise.
    //          输出OC对象，如果OC对象实现了descriptionWithLocale:方法，输出这个方法的返回值，如果没有，输出 description 方法返回值
    // %d,%D    Signed 32-bit integer (int).
    //          10进制输出32位整形
    
    // %u,%U    Unsigned 32-bit integer (unsigned int).
    //          10进制输出32位无符号整形
    // %x       Unsigned 32-bit integer (unsigned int), printed in hexadecimal using the digits 0–9 and lowercase a–f.
    //          16进制输出32位无符号整形，a到f用小写字母
    // %X       Unsigned 32-bit integer (unsigned int), printed in hexadecimal using the digits 0–9 and uppercase A–F.
    //          16进制输出32位无符号整形，A到F用大写字母
    // %o, %O   Unsigned 32-bit integer (unsigned int), printed in octal.
    //          8进制输出32位无符号整形
    
    // %f       64-bit floating-point number (double).
    //          输出64位双精浮点数
    // %F       64-bit floating-point number (double), printed in decimal notation.
    //          输出64位双精浮点数, 10 进制
    // %e       64-bit floating-point number (double), printed in scientific notation using a lowercase e to introduce the exponent.
    //          输出64位双精浮点数，使用科学计数法，其中e使用小写字母
    // %E       64-bit floating-point number (double), printed in scientific notation using a uppercase E to introduce the exponent.
    //          输出64位双精浮点数，使用科学计数法，其中E使用大写字母
    // %g       64-bit floating-point number (double), printed in the style of %e if the exponent is less than –4 or greater than or equal to the precision, in the style of %f otherwise.
    //          输出64位双精浮点数，如果指数e部分的数比-4小，按照 %e 输出，否则按照 %f输出
    // %G       64-bit floating-point number (double), printed in the style of %E if the exponent is less than –4 or greater than or equal to the precision, in the style of %f otherwise.
    //          输出64位双精浮点数，如果指数e部分的数比-4小，按照 %E 输出，否则按照 %f输出
    // %a       64-bit floating-point number (double), printed in scientific notation with a leading 0x and one hexadecimal digit before the decimal point using a lowercase p to introduce the exponent.
    //          输出64位双精浮点数，使用科学计数法，在小数点前使用一位16进制数，0x为先导，使用小写p表示指数部分
    // %A       64-bit floating-point number (double), printed in scientific notation with a leading 0X and one hexadecimal digit before the decimal point using a uppercase P to introduce the exponent.
    //          输出64位双精浮点数，使用科学计数法，在小数点前使用一位16进制数，0X为先导，使用大写P表示指数部分

    

    
    // %c       8-bit unsigned character (unsigned char).
    //          输出字符，适用类型为无符号char
    // %C       16-bit UTF-16 code unit (unichar).
    //          输出UTF-16字符，适用类型为unichar
    
    // %s       Null-terminated array of 8-bit unsigned characters. Because the %s specifier causes the characters to be interpreted in the system default encoding, the results can be variable, especially with right-to-left languages. For example, with RTL, %s inserts direction markers when the characters are not strongly directional. For this reason, it’s best to avoid %s and specify encodings explicitly.
    //          输出以Null结尾的8比特无符号字符为单元的字符数组，使用该格式符，将根据系统默认编码解释字符，所以输出结果不定，特别是从右到左书写的语言。因此最好不要使用%s
    // %S       Null-terminated array of 16-bit UTF-16 code units.
    //          输出以Null结尾的16比特UTF-16字符为单元的字符数组
    
    // %p       Void pointer (void *), printed in hexadecimal with the digits 0–9 and lowercase a–f, with a leading 0x.
    //          （这是小写p）打印void指针地址，使用0x先导，16进制输出，a到f使用小写字母
    
    
    // =========== 长度格式符 =============
    
    // h        Length modifier specifying that a following d, o, u, x, or X conversion specifier applies to a short or unsigned short argument.
    //          h 后面可以跟 d, o, u, x, X，表示在这些特定类型接收一个 shor 或是 unsigned short 参数
    // hh       Length modifier specifying that a following d, o, u, x, or X conversion specifier applies to a signed char or unsigned char argument.
    //          hh 后面可以跟 d, o, u, x, X，表示在这些特定类型接收一个 char 或是 unsigned char 参数
    // l        Length modifier specifying that a following d, o, u, x, or X conversion specifier applies to a long or unsigned long argument.
    //          l 后面可以跟 d, o, u, x, X，表示在这些特定类型接收一个 long 或是 unsigned long 参数
    // ll,q     Length modifiers specifying that a following d, o, u, x, or X conversion specifier applies to a long long or unsigned long long argument.
    //          ll 后面可以跟 d, o, u, x, X，表示在这些特定类型接收一个 long long 或是 unsigned long long参数
    // L        Length modifier specifying that a following a, A, e, E, f, F, g, or G conversion specifier applies to a long double argument.
    //          L 后面可以跟 a, A, e, E, f, F, g, G 表示在这些特定类型接收一个 long double 参数
    // z        Length modifier specifying that a following d, o, u, x, or X conversion specifier applies to a size_t.
    //          z 后面可以跟 d, o, u, x, X 表示在这些特定类型接收一个 size_t 参数
    // t        Length modifier specifying that a following d, o, u, x, or X conversion specifier applies to a ptrdiff_t.
    //          z 后面可以跟 d, o, u, x, X 表示在这些特定类型接收一个 ptrdiff_t 参数
    
    
    /*
     ptrdiff_t是C/C++标准库中定义的一个与机器相关的数据类型。
     ptrdiff_t类型变量通常用来保存两个指针减法操作的结果。
     ptrdiff_t定义在stddef.h（cstddef）这个文件内。ptrdiff_t通常被定义为long int类型。
     ptrdiff_t定义在C99标准中。
     标准库类型(library type)ptrdiff_t 与 size_t 类型一样,ptrdiff_t 也是一种与机器相关的类型,在 cstddef 头文件中定义。
     size_t 是unsigned 类型,而 ptrdiff_t 则是 signed 整型 [1]  。
     这两种类型的差别体现了它们各自的用途：size_t 类型用于指明数组长度,它必须是一个正数;ptrdiff_t 类型则应保证足以存放同一数组中两个指针之间的差距,它有可能是负数 [1]  。
     */
    
    // NSInteger CFIndex    使用 %ld or %lx 格式化输出
    // NSUInteger           使用 %lu or %lx 格式化输出
    // CGFloat              使用 %f or %g 格式化输出
    
    
    
    int numInt32 = -1024;
    
    unsigned int numUInt32 = 1024;
    
    //double numF64 = 3.141592654;
    double numF64 = 31415.92654;
    
    unsigned char numUC8 = 'A';
    
    unichar numInt16 = 0xab;
    
    long numLong = 123456789;
    NSInteger numNSInteger = -123456;
    NSUInteger numNSUInteger = 123456;
    CGFloat numCGFloat = 2.71828182846;
    
    long long numLongLong = 123456789012345;
    unsigned long long numULongLong = 123456789012345;
    
    NSLog(@"输出字符，适用类型为无符号char %%c:%c", numUC8);
    NSLog(@"输出UTF-16字符，适用类型为unichar %%C:%C", numInt16);
    NSLog(@"NSInteger %%ld:%ld %%lx:%lx", numNSInteger, numNSInteger);
    NSLog(@"NSUInteger %%lu:%lu %%lx:%lx", numNSUInteger, numNSUInteger);
    
    
    NSLog(@"10进制输出32位整形 %%d:%d %%D:%D", numInt32, numInt32);
    NSLog(@"10进制输出32位无符号整形 %%u:%u %%U:%U", numUInt32, numUInt32);
    NSLog(@"16进制输出32位无符号整形 %%x:%x %%X:%X", numUInt32, numUInt32);
    NSLog(@"8进制输出32位无符号整形 %%o:%o %%O:%O", numUInt32, numUInt32);
    NSLog(@"输出64位双精浮点数 %%f:%f %%F:%F %%e:%e %%E:%E %%g:%g %%G:%G %%a:%a %%A:%A", numF64, numF64, numF64, numF64, numF64, numF64);
    
    NSLog(@"输出64位双精浮点数 %%.3f:%.3f", numCGFloat);
    
    /*
输出字符，适用类型为无符号char %c:A
输出UTF-16字符，适用类型为unichar %C:«
NSInteger %ld:-123456 %lx:fffffffffffe1dc0
NSUInteger %lu:123456 %lx:1e240
10进制输出32位整形 %d:-1024 %D:4294966272
10进制输出32位无符号整形 %u:1024 %U:1024
16进制输出32位无符号整形 %x:400 %X:400
8进制输出32位无符号整形 %o:2000 %O:2000
输出64位双精浮点数 %f:31415.926540 %F:31415.926540 %e:3.141593e+04 %E:3.141593E+04 %g:31415.9 %G:31415.9 %a:-0x1p-1043 %A:-0X1.FFFF0001FFFEP-1027
    //*/
}

+ (NSString*)makeMD5FromString:(NSString*)oStr {
    if(!oStr) {
        return nil;
    }
    
    const char *cStr = [oStr UTF8String];
    unsigned char digest[CC_MD5_DIGEST_LENGTH];
    
    // MD5 Call
    CC_MD5(cStr, (CC_LONG)strlen(cStr), digest);
    
    NSMutableString *result = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
    
    for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
        [result appendFormat:@"%02X", digest[i]];
    }
    return result;
}

+ (void)stringFormat2MD5 {
    NSString *ostr = @"七律·长征 \
毛泽东 \
1935年10月 \
红军不怕远征难，万水千山只等闲 \
五岭逶迤腾细浪，乌蒙磅礴走泥丸。\
金沙水拍云崖暖，大渡桥横铁索寒。\
更喜岷山千里雪，三军过后尽开颜。";
    
    NSLog(@"从这个字符串提取md5摘要:%@", ostr);
    
    NSString *md5Res = [DemoString makeMD5FromString:ostr];
    NSLog(@"md5Res:%@", md5Res);
    
    md5Res = [DemoString makeMD5FromString:[ostr stringByAppendingString:@" "]];
    NSLog(@"加入一个空格后，md5Res:%@", md5Res);
}

+ (NSString*)makeBase64FromString:(NSString*)oStr {
    
    if(!oStr) {
        return nil;
    }
    
    NSData *data = [oStr dataUsingEncoding:NSUTF8StringEncoding];
    //NSString *stringBase64 = [data base64Encoding]; // base64格式的字符串(不建议使用,用下面方法替代)
    NSString *stringBase64 = [data base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed]; // base64格式的字符串
    
    return stringBase64;
}

+ (NSString*)makeStringFromBase64:(NSString*)base64Str {
    
    if(!base64Str) {
        return nil;
    }
    
    NSData *data = [[NSData alloc] initWithBase64EncodedString:base64Str options:NSDataBase64DecodingIgnoreUnknownCharacters];
    
    NSString *oStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    
    return oStr;
}

+ (void)stringBase64 {
    NSString *ostr = @"七律·长征 \
    毛泽东 \
    1935年10月 \
    红军不怕远征难，万水千山只等闲 \
    五岭逶迤腾细浪，乌蒙磅礴走泥丸。\
    金沙水拍云崖暖，大渡桥横铁索寒。\
    更喜岷山千里雪，三军过后尽开颜。";
    
    NSLog(@"从这个字符串做base64编码:%@", ostr);
    
    NSString *base64Res = [DemoString makeBase64FromString:ostr];
    NSLog(@"base64Res:%@", base64Res);
    
    NSString *strFromBase64 = [DemoString makeStringFromBase64:base64Res];
    NSLog(@"再从base64编码解码出来:%@", strFromBase64);
    
    if([ostr isEqualToString:strFromBase64]) {
        NSLog(@"base64编码解码后 和源字符串一样");
    } else {
        NSLog(@"base64编码解码后 和源字符串 不一样");
    }
}

// AES256 nsdata 加密
// https://www.jianshu.com/p/0f941a79ed99
+ (NSData *)makeAES256EncryptData:(NSData *)sourceData key:(NSString *)key {
    
    char keyPtr[kCCKeySizeAES256 + 1];
    bzero(keyPtr, sizeof(keyPtr));
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
    
    NSUInteger dataLength = [sourceData length];
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);
    size_t numBytesEncrypted = 0;
    
    //    CCCryptorStatus cryptStatus1 = CCCrypt(<#CCOperation op#>, <#CCAlgorithm alg#>, <#CCOptions options#>, <#const void *key#>, <#size_t keyLength#>, <#const void *iv#>, <#const void *dataIn#>, <#size_t dataInLength#>, <#void *dataOut#>, <#size_t dataOutAvailable#>, <#size_t *dataOutMoved#>)
    
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
                                          kCCAlgorithmAES128,
                                          kCCOptionPKCS7Padding | kCCOptionECBMode,
                                          keyPtr,
                                          kCCBlockSizeAES128,
                                          NULL,
                                          [sourceData bytes],
                                          dataLength,
                                          buffer,
                                          bufferSize,
                                          &numBytesEncrypted);
    
    if (cryptStatus == kCCSuccess) {
        // 加密成功，从buffer 构建data 返回
        return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
    }
    
    free(buffer);
    return nil;
}

// AES256 nsstring 加密
+ (NSString *)makeAES256EncryptStr:(NSString *)oStr key:(NSString *)key {
    
    const char *cstr = [oStr cStringUsingEncoding:NSUTF8StringEncoding];
    // https://www.jianshu.com/p/0f941a79ed99 里面，下面一行代码用的是 [oStr length]，这样用，中文内容会gg的，要改成 strlen(cstr) 才可以
    NSData *oData = [NSData dataWithBytes:cstr length:strlen(cstr)];
    
    [DemoString printBytes:oData];
    
    // 对nsdata进行加密
    NSData *encryptData = [DemoString makeAES256EncryptData:oData key:key];
    
    //转换为2进制字符串
    if (encryptData && encryptData.length > 0) {

        Byte *datas = (Byte*)[encryptData bytes];
        NSMutableString *output = [NSMutableString stringWithCapacity:encryptData.length * 2];
        for(int i = 0; i < encryptData.length; i++){
            [output appendFormat:@"%02x", datas[i]];
        }
        return output;
    }
    
    return nil;
}

// AES256 nsdata 解密
// https://www.jianshu.com/p/0f941a79ed99
+ (NSData *)makeAES256DecryptData:(NSData *)encryptData key:(NSString *)key {
    char keyPtr[kCCKeySizeAES256+1];
    bzero(keyPtr, sizeof(keyPtr));
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
    
    NSUInteger dataLength = [encryptData length];
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);
    size_t numBytesDecrypted = 0;
    
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
                                          kCCAlgorithmAES128,
                                          kCCOptionPKCS7Padding | kCCOptionECBMode,
                                          keyPtr,
                                          kCCBlockSizeAES128,
                                          NULL,
                                          [encryptData bytes],
                                          dataLength,
                                          buffer,
                                          bufferSize,
                                          &numBytesDecrypted);
    
    if (cryptStatus == kCCSuccess) {
        // 解密成功，从buffer 构建data 返回
        return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
    }
    
    free(buffer);
    return nil;
}

// AES256 nsstring 解密
+ (NSString *)makeAES256DecryptString:(NSString *)encryptStr key:(NSString *)key {
    
    //转换为2进制Data
    NSMutableData *encryptData = [NSMutableData dataWithCapacity:encryptStr.length / 2];
    unsigned char whole_byte;
    char byte_chars[3] = {'\0','\0','\0'};
    int i;
    for (i=0; i < [encryptStr length] / 2; i++) {
        byte_chars[0] = [encryptStr characterAtIndex:i*2];
        byte_chars[1] = [encryptStr characterAtIndex:i*2+1];
        whole_byte = strtol(byte_chars, NULL, 16);
        [encryptData appendBytes:&whole_byte length:1];
    }
    
    //对数据进行解密
    NSData* decryptRes = [DemoString makeAES256DecryptData:encryptData key:key];
    if (decryptRes && decryptRes.length > 0) {
        
        return [[NSString alloc] initWithData:decryptRes encoding:NSUTF8StringEncoding];
    }
    return nil;
}

+ (void)printBytes:(NSData *)data {
    if (data && data.length > 0) {
        
        Byte *datas = (Byte*)[data bytes];
        NSMutableString *output = [NSMutableString stringWithCapacity:data.length * 2];
        for(int i = 0; i < data.length; i++){
            [output appendFormat:@"%02x", datas[i]];
        }
        NSLog(@"printBytes %@: %@", data, output);
    }
}

+ (void)stringAES {
    NSString *ostr = @"七律·长征 \
    毛泽东 \
    1935年10月 \
    红军不怕远征难，万水千山只等闲 \
    五岭逶迤腾细浪，乌蒙磅礴走泥丸。\
    金沙水拍云崖暖，大渡桥横铁索寒。\
    更喜岷山千里雪，三军过后尽开颜。";
    
    //NSString *aesKey=@"人民英雄";
    NSString *aesKey=@"renminyingxiong";
    
    NSLog(@"从这个字符串做AES加密:%@", ostr);
    
    NSString *aesResEncrypt = [DemoString makeAES256EncryptStr:ostr key:aesKey];
    NSLog(@"aesResEncrypt:%@", aesResEncrypt);
    
    NSString *strDecrypt = [DemoString makeAES256DecryptString:aesResEncrypt key:aesKey];
    NSLog(@"再从AES编码解码出来:%@", strDecrypt);
    
    if([ostr isEqualToString:strDecrypt]) {
        NSLog(@"AES编码解码后 和源字符串一样");
    } else {
        NSLog(@"AES编码解码后 和源字符串 不一样");
    }
    
    ostr = @"Never in the field of human conflict was so much owed by so many to so few";
    aesKey=@"Churchill";
    NSLog(@"从这个字符串做AES加密:%@", ostr);
    
    aesResEncrypt = [DemoString makeAES256EncryptStr:ostr key:aesKey];
    NSLog(@"aesResEncrypt:%@", aesResEncrypt);
    
    strDecrypt = [DemoString makeAES256DecryptString:aesResEncrypt key:aesKey];
    NSLog(@"再从AES编码解码出来:%@", strDecrypt);
    
    if([ostr isEqualToString:strDecrypt]) {
        NSLog(@"AES编码解码后 和源字符串一样");
    } else {
        NSLog(@"AES编码解码后 和源字符串 不一样");
    }
}

+ (void)demoRegLogic {
    NSString *res = [DemoString taopasswordMatchResultWithRegex:@"[￥](.*?)[￥]|[()/￥€£₴$₰¢₤¥₳₲₪₵《\\ud83d\\udd11\\ud83d\\udddd\\ud83d\\ude3a\\ud83d\\ude38\\ud83c\\udf81\\ud83d\\udcf2\\ud83d\\udcb0\\ud83d\\udcb2\\u2714\\ud83c\\udfb5\\ud83d\\udd10][A-Za-z0-9]{7,15}[()/￥€£₴$₰¢₤¥₳₲₪₵《\\ud83d\\udd11\\ud83d\\udddd\\ud83d\\ude3a\\ud83d\\ude38\\ud83c\\udf81\\ud83d\\udcf2\\ud83d\\udcb0\\ud83d\\udcb2\\u2714\\ud83c\\udfb5\\ud83d\\udd10]|[你我谁这推]{1}((.){15,25})!\\.!" matchString:@"我你sssssssss1111122!.!"];
    
    NSLog(@"res = %@", res);
    
    res = [DemoString taopasswordMatchResultWithRegex:@"[你我谁这推]{1}((.){15,25})!.!" matchString:@"我今天心情很好,哈哈哈哈哈哈啊哈哈!.!"];
    
    NSLog(@"res = %@", res);
}

+ (void)regexExtract {
    NSString *dtStr = @"2023-04-04T07:19:35.453+00:00";
    // 使用正则，从中提取 年月日，时分秒，毫秒，时区
    
    // NSRegularExpression
    NSError *err;
    NSRegularExpression *regular = [[NSRegularExpression alloc] initWithPattern:@"(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2}).(\\d{3})\\+(\\d{2}):(\\d{2})" options:NSRegularExpressionCaseInsensitive error:&err];
    
    // 返回一个存有所有符合约定的字段的NSArray
    NSArray *results = [regular matchesInString:dtStr options:0 range:NSMakeRange(0, dtStr.length)];
    NSLog(@"results : %ld", results.count);
    if(results.count > 0) {
        NSUInteger index = 0;
        for(NSTextCheckingResult *tcr in results) {
            ;
            NSLog(@"tcr, %ld, %@, numberOfRanges:%ld", index, [dtStr substringWithRange:tcr.range], [tcr numberOfRanges]);
            for (NSUInteger i=1;i<[tcr numberOfRanges];i+=1) {
                NSRange groupRange = [tcr rangeAtIndex:i];
                NSLog(@"  group:%ld, %@", i, [dtStr substringWithRange:groupRange]);
            }
            ++index;
        }
    }
    
}

+ (void)demoLogic {
    [DemoString stringFromToDate];
    
    [DemoString stringFromToArray];
    
    [DemoString subStringSearch];
    
    [DemoString regexSearch];
    
    [DemoString stringJoin];
    
    [DemoString stringTrim];
    
    [DemoString stringFormat];
    
    [DemoString stringFormat2MD5];
    
    [DemoString stringBase64];
    
    [DemoString stringAES];
    
    [DemoString regexExtract];
}
@end
